<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>flask 源码解析：响应 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="flask 源码解析：响应 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/01/22/flask-insight-response/">

	
	<meta property="article:published_time" content="2017-01-22T00:01:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/01/22/flask-insight-response/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-01-21T16:00:00.000Z" itemprop="datePublished">
                    2017-01-22
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">flask 源码解析：响应</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>这是 flask 源码解析系列文章的其中一篇，本系列所有文章列表：</p>
<ul>
<li><a href="http://cizixs.com/2017/01/10/flask-insight-introduction">flask 源码解析：简介</a></li>
<li><a href="http://cizixs.com/2017/01/11/flask-insight-start-process">flask 源码解析：应用启动流程</a></li>
<li><a href="http://cizixs.com/2017/01/12/flask-insight-routing">flask 源码解析：路由</a></li>
<li><a href="http://cizixs.com/2017/01/13/flask-insight-context">flask 源码解析：上下文</a></li>
<li><a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 源码解析：请求</a></li>
<li><a href="http://cizixs.com/2017/01/22/flask-insight-response">flask 源码解析：响应</a></li>
<li><a href="http://cizixs.com/2017/03/08/flask-insight-session">flask 源码解析：session</a></li>
</ul>
<h2 id="response-简介"><a href="#response-简介" class="headerlink" title="response 简介"></a>response 简介</h2><p>在 flask 应用中，我们只需要编写 view 函数，并不需要直接和响应（response）打交道，flask 会自动生成响应返回给客户端。</p>
<blockquote>
<p>The return value from a view function is automatically converted into a response object for you.<br>—— Flask docs</p>
</blockquote>
<p>我们知道 HTTP 响应分为三个部分：<br>状态栏（HTTP 版本、状态码和说明）、头部（以冒号隔开的字符对，用于各种控制和协商）、body（服务端返回的数据）。比如下面访问<a href="http://cizixs.com">博客首页</a>的响应：</p>
<pre class=" language-bash"><code class="language-bash">HTTP/1.1 200 OK

Access-Control-Allow-Origin: *
Cache-Control: max-age<span class="token operator">=</span>600
Content-Encoding: <span class="token function">gzip</span>
Content-Type: text/html<span class="token punctuation">;</span> charset<span class="token operator">=</span>utf-8
Date: Wed, 15 Feb 2017 07:50:41 GMT
Expires: Wed, 15 Feb 2017 08:00:41 GMT
Last-Modified: Wed, 15 Feb 2017 07:46:56 GMT
Server: GitHub.com
Transfer-Encoding: chunked
X-GitHub-Request-Id: D2A7:7B6B:33C0628:47C44B9:58A40851

<span class="token operator">&lt;</span>BODY<span class="token operator">></span>
</code></pre>
<p>flask 自然也会提供所有这些数据的操作，视图函数就支持返回三个值：第一个是返回的数据，第二个是状态码，第三个是头部字典。比如：</p>
<pre class=" language-python"><code class="language-python">@app<span class="token punctuation">.</span>route<span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">hello_world</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token string">'Hello, World!'</span><span class="token punctuation">,</span> <span class="token number">201</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">'X-Foo'</span><span class="token punctuation">:</span> <span class="token string">'bar'</span><span class="token punctuation">}</span>
</code></pre>
<p>这篇文章就讲讲这背后的魔法。</p>
<h2 id="flask-响应（response）"><a href="#flask-响应（response）" class="headerlink" title="flask 响应（response）"></a>flask 响应（response）</h2><p>在 <a href="http://cizixs.com/2017/01/11/flask-insight-start-process">flask 源码解析：应用启动流程</a> 的最后，我们讲到 <code>full_dispatch_request</code> 在调用路由的视图函数之后，会调用 <code>finalize_request</code> 进行最后的处理，在这个方法里就包含了 response 对象的生成和处理逻辑。</p>
<p><code>finalize_request</code> 的代码如下：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">finalize_request</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> rv<span class="token punctuation">,</span> from_error_handler<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Given the return value from a view function this finalizes
    the request by converting it into a response and invoking the
    postprocessing functions.  This is invoked for both normal
    request dispatching as well as error handlers.
    """</span>
    response <span class="token operator">=</span> self<span class="token punctuation">.</span>make_response<span class="token punctuation">(</span>rv<span class="token punctuation">)</span>
    <span class="token keyword">try</span><span class="token punctuation">:</span>
        response <span class="token operator">=</span> self<span class="token punctuation">.</span>process_response<span class="token punctuation">(</span>response<span class="token punctuation">)</span>
        request_finished<span class="token punctuation">.</span>send<span class="token punctuation">(</span>self<span class="token punctuation">,</span> response<span class="token operator">=</span>response<span class="token punctuation">)</span>
    <span class="token keyword">except</span> Exception<span class="token punctuation">:</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> from_error_handler<span class="token punctuation">:</span>
            <span class="token keyword">raise</span>
        self<span class="token punctuation">.</span>logger<span class="token punctuation">.</span>exception<span class="token punctuation">(</span><span class="token string">'Request finalizing failed with an '</span>
                              <span class="token string">'error while handling an error'</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> response
</code></pre>
<p>里面有两个方法调用：<code>make_response</code> 根据视图函数的返回值生成 response 对象，<code>process_response</code> 对 response 做一些后续的处理（比如执行 hooks 函数）。我们先来看看 <code>make_response</code>：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">make_response</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> rv<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Converts the return value from a view function to a real
    response object that is an instance of :attr:`response_class`.
    """</span>
    status_or_headers <span class="token operator">=</span> headers <span class="token operator">=</span> None
    <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>rv<span class="token punctuation">,</span> tuple<span class="token punctuation">)</span><span class="token punctuation">:</span>
        rv<span class="token punctuation">,</span> status_or_headers<span class="token punctuation">,</span> headers <span class="token operator">=</span> rv <span class="token operator">+</span> <span class="token punctuation">(</span>None<span class="token punctuation">,</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator">-</span> len<span class="token punctuation">(</span>rv<span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>status_or_headers<span class="token punctuation">,</span> <span class="token punctuation">(</span>dict<span class="token punctuation">,</span> list<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        headers<span class="token punctuation">,</span> status_or_headers <span class="token operator">=</span> status_or_headers<span class="token punctuation">,</span> None

    <span class="token keyword">if</span> <span class="token operator">not</span> isinstance<span class="token punctuation">(</span>rv<span class="token punctuation">,</span> self<span class="token punctuation">.</span>response_class<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># When we create a response object directly, we let the constructor</span>
        <span class="token comment" spellcheck="true"># set the headers and status.  We do this because there can be</span>
        <span class="token comment" spellcheck="true"># some extra logic involved when creating these objects with</span>
        <span class="token comment" spellcheck="true"># specific values (like default content type selection).</span>
        <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>rv<span class="token punctuation">,</span> <span class="token punctuation">(</span>text_type<span class="token punctuation">,</span> bytes<span class="token punctuation">,</span> bytearray<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
            rv <span class="token operator">=</span> self<span class="token punctuation">.</span>response_class<span class="token punctuation">(</span>rv<span class="token punctuation">,</span> headers<span class="token operator">=</span>headers<span class="token punctuation">,</span>
                                     status<span class="token operator">=</span>status_or_headers<span class="token punctuation">)</span>
            headers <span class="token operator">=</span> status_or_headers <span class="token operator">=</span> None

    <span class="token keyword">if</span> status_or_headers <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
        <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>status_or_headers<span class="token punctuation">,</span> string_types<span class="token punctuation">)</span><span class="token punctuation">:</span>
            rv<span class="token punctuation">.</span>status <span class="token operator">=</span> status_or_headers
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            rv<span class="token punctuation">.</span>status_code <span class="token operator">=</span> status_or_headers
    <span class="token keyword">if</span> headers<span class="token punctuation">:</span>
        rv<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>extend<span class="token punctuation">(</span>headers<span class="token punctuation">)</span>

    <span class="token keyword">return</span> rv
</code></pre>
<p><code>make_response</code> 是视图函数能返回多个不同数量和类型值的关键，因为它能处理这些情况，统一把它们转换成 response。<br>如果返回值本身就是 Response 实例，就直接使用它；如果返回值是字符串类型，就把它作为响应的 body，并自动设置状态码和头部信息；<br>如果返回值是 tuple，会尝试用 (response, status, headers) 或者 (response, headers) 去解析。</p>
<p><strong>NOTE</strong>：因为视图函数可以返回 <code>Response</code> 对象，因此我们可以直接操作 <code>Response</code>。</p>
<p>不管视图函数返回的是什么，最终都会变成 <code>Response</code> 对象，那么我们就来看看 <code>Response</code> 的定义：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">from</span> werkzeug<span class="token punctuation">.</span>wrappers <span class="token keyword">import</span> Response <span class="token keyword">as</span> ResponseBase


<span class="token keyword">class</span> <span class="token class-name">Response</span><span class="token punctuation">(</span>ResponseBase<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""The response object that is used by default in Flask.  Works like the
    response object from Werkzeug but is set to have an HTML mimetype by
    default.  Quite often you don't have to create this object yourself because
    :meth:`~flask.Flask.make_response` will take care of that for you.

    If you want to replace the response object used you can subclass this and
    set :attr:`~flask.Flask.response_class` to your subclass.
    """</span>
    default_mimetype <span class="token operator">=</span> <span class="token string">'text/html'</span>
</code></pre>
<p>Flask 的 <code>Response</code> 类非常简单，它只是继承了 <code>werkzeug.wrappers:Response</code>，然后设置默认返回类型为 html。<br>不过从注释中，我们得到两条很有用的信息：</p>
<ol>
<li>一般情况下不要直接操作 <code>Response</code> 对象，而是使用 <code>make_response</code> 方法来生成它</li>
<li>如果需要使用自定义的响应对象，可以覆盖 flask app 对象的 <code>response_class</code> 属性。</li>
</ol>
<p>继续，下面就要分析 werkzeug 对应的代码了。</p>
<h2 id="werkzeug-response"><a href="#werkzeug-response" class="headerlink" title="werkzeug response"></a>werkzeug response</h2><p>werkzeug 实现的 response 定义在 <code>werkzeug/wrappers.py</code> 文件中：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Response</span><span class="token punctuation">(</span>BaseResponse<span class="token punctuation">,</span> ETagResponseMixin<span class="token punctuation">,</span> ResponseStreamMixin<span class="token punctuation">,</span>
               CommonResponseDescriptorsMixin<span class="token punctuation">,</span>
               WWWAuthenticateMixin<span class="token punctuation">)</span><span class="token punctuation">:</span>

    <span class="token triple-quoted-string string">"""Full featured response object implementing the following mixins:

    - :class:`ETagResponseMixin` for etag and cache control handling
    - :class:`ResponseStreamMixin` to add support for the `stream` property
    - :class:`CommonResponseDescriptorsMixin` for various HTTP descriptors
    - :class:`WWWAuthenticateMixin` for HTTP authentication support
    """</span>
</code></pre>
<p>和我们在 <a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 请求</a>分析的 Request 类一样，这里使用了 Mixin 机制。<code>BaseResponse</code> 精简后的大概框架如下：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">BaseResponse</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Base response class.  The most important fact about a response object
    is that it's a regular WSGI application.  It's initialized with a couple
    of response parameters (headers, body, status code etc.) and will start a
    valid WSGI response when called with the environ and start response
    callable.
    """</span>

    charset <span class="token operator">=</span> <span class="token string">'utf-8'</span>
    default_status <span class="token operator">=</span> <span class="token number">200</span>
    default_mimetype <span class="token operator">=</span> <span class="token string">'text/plain'</span>
    automatically_set_content_length <span class="token operator">=</span> <span class="token boolean">True</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> response<span class="token operator">=</span>None<span class="token punctuation">,</span> status<span class="token operator">=</span>None<span class="token punctuation">,</span> headers<span class="token operator">=</span>None<span class="token punctuation">,</span>
                 mimetype<span class="token operator">=</span>None<span class="token punctuation">,</span> content_type<span class="token operator">=</span>None<span class="token punctuation">,</span> direct_passthrough<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">pass</span>
</code></pre>
<p><code>BaseResponse</code> 有一些类属性，定义了默认的值，比如默认字符编码是 utf-8，默认状态码是 200 等。实例化的时候接受的参数有：</p>
<ul>
<li>response： 字符串或者其他 iterable 对象，作为响应的 body</li>
<li>status： 状态码，可以是整数，也可以是字符串</li>
<li>headers： 响应的头部，可以是个列表，也可以是 <code>werkzeug.datastructures.Headers</code> 对象</li>
<li>mimetype： mimetype 类型，告诉客户端响应 body 的格式，默认是文本格式</li>
<li>content_type: 响应头部的 <code>Content-Type</code> 内容</li>
</ul>
<p>所有这些参数都是可选的，默认情况下会生成一个状态码为 200，没有任何 body 的响应。status、status_code 作为 <code>Response</code> 的属性，可以直接读取和修改。body 数据在内部保存为 iterable 的类型，<br>但是对外也提供了直接读写的接口 <code>self.data</code>：</p>
<pre class=" language-python"><code class="language-python">    <span class="token keyword">def</span> <span class="token function">get_data</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> as_text<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The string representation of the request body.  Whenever you call
        this property the request iterable is encoded and flattened.
        """</span>
        self<span class="token punctuation">.</span>_ensure_sequence<span class="token punctuation">(</span><span class="token punctuation">)</span>
        rv <span class="token operator">=</span> b<span class="token string">''</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>self<span class="token punctuation">.</span>iter_encoded<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> as_text<span class="token punctuation">:</span>
            rv <span class="token operator">=</span> rv<span class="token punctuation">.</span>decode<span class="token punctuation">(</span>self<span class="token punctuation">.</span>charset<span class="token punctuation">)</span>
        <span class="token keyword">return</span> rv

    <span class="token keyword">def</span> <span class="token function">set_data</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Sets a new string as response.  The value set must either by a
        unicode or bytestring.
        """</span>
        <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>value<span class="token punctuation">,</span> text_type<span class="token punctuation">)</span><span class="token punctuation">:</span>
            value <span class="token operator">=</span> value<span class="token punctuation">.</span>encode<span class="token punctuation">(</span>self<span class="token punctuation">.</span>charset<span class="token punctuation">)</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            value <span class="token operator">=</span> bytes<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>response <span class="token operator">=</span> <span class="token punctuation">[</span>value<span class="token punctuation">]</span>
        <span class="token keyword">if</span> self<span class="token punctuation">.</span>automatically_set_content_length<span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>headers<span class="token punctuation">[</span><span class="token string">'Content-Length'</span><span class="token punctuation">]</span> <span class="token operator">=</span> str<span class="token punctuation">(</span>len<span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span>

    data <span class="token operator">=</span> property<span class="token punctuation">(</span>get_data<span class="token punctuation">,</span> set_data<span class="token punctuation">,</span> doc<span class="token operator">=</span><span class="token triple-quoted-string string">'''
        A descriptor that calls :meth:`get_data` and :meth:`set_data`.  This
        should not be used and will eventually get deprecated.
        '''</span><span class="token punctuation">)</span>
</code></pre>
<p>body 字符的编码和长度都是自动设置的，用户不需要手动处理。</p>
<p>至于头部的存储，werkzeug 使用的是类似于字典的 <code>werkzeug.datastructures:Headers</code> 类。在<a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 源码解析：请求</a>这篇文章中，我们没有详细<br>解释头部的存储，那么这篇文章就具体分析一下吧。</p>
<p><code>Headers</code> 这个类的提供了很多和字典相同的接口：keys、values、iterms，但是和字典的区别在于它保存的值是有序的，而且允许相同 key 的值存在。<br>为什么这么设计呢？因为着更符合 HTTP 头部的特性。先来看看有序，在 HTTP 传送的过程中，如果头部各个 key-value 键值对顺序发生变化，有些代理或者客户端等组件会认为请求被篡改而丢弃或者拒绝请求的处理，所以最好把头部设置为有序的，用户按照什么顺序设置的，就按照什么顺序存储；再说说相同 key 的问题，这是因为 HTTP 头部同一个 key 可能有多个 value（比如 Accept、SetCookie头部）。那么这个看起比较特殊的字典是怎么实现的呢？来看代码：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Headers</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""An object that stores some headers.  It has a dict-like interface
    but is ordered and can store the same keys multiple times.
    """</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> defaults<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>_list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
        <span class="token keyword">if</span> defaults <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>defaults<span class="token punctuation">,</span> <span class="token punctuation">(</span>list<span class="token punctuation">,</span> Headers<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                self<span class="token punctuation">.</span>_list<span class="token punctuation">.</span>extend<span class="token punctuation">(</span>defaults<span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                self<span class="token punctuation">.</span>extend<span class="token punctuation">(</span>defaults<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__getitem__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">,</span> _get_mode<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> _get_mode<span class="token punctuation">:</span>
            <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>key<span class="token punctuation">,</span> integer_types<span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">return</span> self<span class="token punctuation">.</span>_list<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
            <span class="token keyword">elif</span> isinstance<span class="token punctuation">(</span>key<span class="token punctuation">,</span> slice<span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">return</span> self<span class="token punctuation">.</span>__class__<span class="token punctuation">(</span>self<span class="token punctuation">.</span>_list<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> isinstance<span class="token punctuation">(</span>key<span class="token punctuation">,</span> string_types<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">raise</span> exceptions<span class="token punctuation">.</span>BadRequestKeyError<span class="token punctuation">(</span>key<span class="token punctuation">)</span>
        ikey <span class="token operator">=</span> key<span class="token punctuation">.</span>lower<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">for</span> k<span class="token punctuation">,</span> v <span class="token keyword">in</span> self<span class="token punctuation">.</span>_list<span class="token punctuation">:</span>
            <span class="token keyword">if</span> k<span class="token punctuation">.</span>lower<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> ikey<span class="token punctuation">:</span>
                <span class="token keyword">return</span> v
        <span class="token keyword">if</span> _get_mode<span class="token punctuation">:</span>
            <span class="token keyword">raise</span> KeyError<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">raise</span> exceptions<span class="token punctuation">.</span>BadRequestKeyError<span class="token punctuation">(</span>key<span class="token punctuation">)</span>
</code></pre>
<p>可以看到，头部信息在内部存储为二元组构成的列表，这样就能同时保证它的有序性和重复性。一个核心的方法是 <code>__getitem__</code>，它定义了如何获取头部中的信息：</p>
<ul>
<li>通过下标 <code>header[3]</code>，直接返回对应未知存储的键值对元组</li>
<li>通过 key，返回 value <code>header[&#39;Accept&#39;]</code>，返回匹配的第一个 value 值</li>
<li>通过 slice <code>header[3:7]</code>，返回另外一个 <code>Headers</code> 对象，保存了 slice 中所有的数据</li>
</ul>
<p>然后实现 <code>keys()</code>、<code>items()</code>、<code>pop()</code>、<code>setdefault()</code> 等方法让它表现出来字典的特性，除此之外还有 <code>add()</code>、<code>extend()</code>、<code>add_header()</code> 等和字典无关的方法方便操作。</p>
<h2 id="自定义-response"><a href="#自定义-response" class="headerlink" title="自定义 response"></a>自定义 response</h2><p>如果需要扩展 flask <code>Response</code> 的功能，或者干脆把它替换掉，只要修改 flask app 的 <code>response_class</code> 属性就可以了，比如：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">from</span> flask <span class="token keyword">import</span> Flask<span class="token punctuation">,</span> Response

<span class="token keyword">class</span> <span class="token class-name">MyResponse</span><span class="token punctuation">(</span>Response<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>

app <span class="token operator">=</span> Flask<span class="token punctuation">(</span>__name__<span class="token punctuation">)</span>
app<span class="token punctuation">.</span>response_class <span class="token operator">=</span> MyResponse
</code></pre>
<p>更多可能的用法，可以参考文章末尾的参考资料。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://blog.miguelgrinberg.com/post/customizing-the-flask-response-class" target="_blank" rel="noopener">customizing the flask response class</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="linux 网络虚拟化： network namespace 简介" href="/2017/02/10/network-virtualization-network-namespace/">
        ← linux 网络虚拟化： network namespace 简介
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="flask 源码解析：请求" href="/2017/01/18/flask-insight-request/">
        flask 源码解析：请求 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#response-简介"><span class="toc-text">response 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#flask-响应（response）"><span class="toc-text">flask 响应（response）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#werkzeug-response"><span class="toc-text">werkzeug response</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#自定义-response"><span class="toc-text">自定义 response</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">flask 源码解析：响应</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/01/22/flask-insight-response/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/01/22/flask-insight-response/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
